import 'dart:async';
import 'dart:io';

import 'package:event_bus/event_bus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:jpush_flutter/jpush_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:shirne_app/utils/core.dart';
import 'package:shirne_dialog/shirne_dialog.dart';

import 'config.dart';
import 'models/member_model.dart';
import 'models/siteinfo_model.dart';
import 'models/category_model.dart';
import 'pages/login/index.dart' as auth;
import 'pages/member/password.dart';
import 'pages/member/profile.dart';
import 'pages/member/setting.dart';
import 'pages/web.dart';
import 'utils/logger.dart';
import 'utils/request.dart';
import 'utils/login.dart' show Login;
import 'pages/index.dart';
import 'pages/swipe.dart';
import 'pages/home/home.dart';
import 'pages/channel/index.dart' as channel;
import 'pages/channel/detail.dart' as channel;
import 'pages/channel/list.dart' as channel;
import 'pages/channel/comment.dart' as channel;
import 'pages/feedback/index.dart' as feedback;
import 'pages/about/index.dart' as about;
import 'pages/member/index.dart' as member;

class DataEvent<T> {
  T? data;

  DataEvent(this.data);

  @override
  String toString() {
    return "DataEvent<$T $data>";
  }
}

class MemberEvent extends DataEvent<MemberModel> {
  MemberEvent(MemberModel? data) : super(data);
}

class SiteinfoEvent extends DataEvent<SiteinfoModel> {
  SiteinfoEvent(SiteinfoModel? data) : super(data);
}

class CartCountEvent extends DataEvent<int> {
  CartCountEvent(int? data) : super(data);
}

class LoginStateEvent extends DataEvent<bool> {
  LoginStateEvent(bool? data) : super(data);
}

class RouteEvent extends DataEvent<String> {
  RouteEvent(String? data) : super(data);
}

class CategoryEvent extends DataEvent<Map<int, List<CategoryModel>>> {
  CategoryEvent(Map<int, List<CategoryModel>>? data) : super(data);
}

final app = Application();

final navigatorKey = MyDialog.navigatorKey;

final globalL10n = navigatorKey.currentContext?.l10n;

class Application extends EventBus {
  late Login login;
  late SharedPreferences spref;
  bool isConnected = false;

  Map<String, dynamic> pageCaches = {};

  Map<int, List<CategoryModel>>? _categories;
  late Request _categoriesRequest;
  MemberModel? _member;
  late Request _memberRequest;
  SiteinfoModel? _siteinfo;
  late Request _siteinfoRequest;
  int _cartCount = 0;
  bool _isLogin = false;

  String _rootRoute = '/';
  late Map<String, WidgetBuilder> routes;
  late Map<String, String> redirects;

  JPush? jPush;
  String? regId;

  bool initialized = false;

  BuildContext get context => MyDialog.navigatorKey.currentContext!;
  NavigatorState get navigator => MyDialog.navigatorKey.currentState!;

  static Future<Application> getInstance() async {
    if (!app.initialized) {
      await app.init();
    }
    return app;
  }

  init() async {
    if (initialized) return;
    initialized = true;

    routes = {
      '/': (context) => const IndexPage(),
      '/home': (context) => const HomePage(),
      '/swipe': (context) => const SwipePage(),
      '/web': (context) => WebPage(getArg(context)!),
      '/channel': (context) =>
          channel.IndexPage(channel: getArg(context)!['channel'] ?? ''),
      '/channel/list': (context) =>
          channel.ListPage(arguments: getArg(context)!),
      '/channel/detail': (context) =>
          channel.DetailPage(arguments: getArg(context)!),
      '/channel/comment': (context) => channel.CommentPage(
            arguments: getArg(context)!,
          ),
      '/feedback': (context) => const feedback.IndexPage(),
      '/about': (context) => const about.IndexPage(),
      '/login': (context) => const auth.IndexPage(),
      '/member': (context) => const member.IndexPage(),
      '/setting': (context) => const SettingPage(),
      '/member/profile': (context) => const ProfilePage(),
      '/member/password': (context) => const PasswordPage(),
    };
    redirects = {
      '/article': '/channel',
      '/article/list': '/channel/list',
      '/article/detail': '/channel/detail',
    };

    spref = await SharedPreferences.getInstance();

    login = Login(this);

    _memberRequest = Request('member/profile');

    _siteinfoRequest = Request('common/siteinfo');

    _categoriesRequest = Request('article/get_all_cates');

    startJPush();
  }

  Map<String, dynamic>? getArg(BuildContext context) {
    return ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>?;
  }

  dispose() {
    if (!initialized) return;

    pageCaches.clear();

    initialized = false;
  }

  Future<SiteinfoModel> getSiteinfo({bool force = false}) async {
    if (!force && _siteinfo != null && _siteinfo!.name.isNotEmpty) {
      return _siteinfo!;
    }
    var sJson = await _siteinfoRequest.getData(force: force);
    _siteinfo = SiteinfoModel.fromJson(sJson);
    fire(SiteinfoEvent(_siteinfo));
    return _siteinfo!;
  }

  Future<MemberModel> getProfile({bool force = false}) async {
    if (!force && _member != null && _member!.id > 0) {
      return _member!;
    }
    var mJson = await _memberRequest.getData(force: force);
    _member = MemberModel.fromJson(mJson);
    fire(MemberEvent(_member));
    return _member!;
  }

  Future<Map<int, List<CategoryModel>>> getCategories({
    bool force = false,
  }) async {
    if (!force && _categories != null) {
      return _categories!;
    }
    var mJson = await _categoriesRequest.getData(force: force);
    _categories = CategoryModel.mapTree(mJson);
    fire(CategoryEvent(_categories));
    return _categories!;
  }

  set isLogin(bool newValue) {
    _isLogin = newValue;
    fire(LoginStateEvent(_isLogin));
  }

  bool get isLogin => _isLogin;

  set cartCount(int newValue) {
    _cartCount = newValue;
    fire(CartCountEvent(_cartCount));
  }

  int get cartCount => _cartCount;

  void cachePage(String name, dynamic data) {
    pageCaches[name] = data;
  }

  dynamic getPage(String name) {
    return pageCaches.containsKey(name) ? pageCaches[name] : null;
  }

  void back([BuildContext? context]) {
    if (navigator.canPop()) {
      navigator.pop();
    } else {
      navigator.pushReplacementNamed('/');
    }
  }

  Future<dynamic> route(
    String url, {
    Map<String, dynamic>? arguments,
    String? channel,
  }) async {
    if (url.indexOf('/pages/') == 0) {
      url = url.substring(6);
    }
    arguments ??= {};
    if (url.indexOf('?') > 0) {
      List<String> parts = url.split('?');
      url = parts[0];
      List<String> query = parts[1].split('&');
      for (var item in query) {
        int idx = item.indexOf('=');
        if (idx > 0) {
          arguments[item.substring(0, idx)] = item.substring(idx + 1);
        }
      }
    }
    if (channel != null) {
      arguments['channel'] = channel;
    }
    //不在路由范围内再尝试去除尾部 index
    if (!routes.containsKey(url) && !redirects.containsKey(url)) {
      if (url.endsWith('/index')) {
        url = url.substring(0, url.length - 6);
      }
    }
    logger.info(['Navigate: $url', arguments]);
    if (redirects.containsKey(url)) {
      logger.info('route redirect:$url');
      route(redirects[url]!, arguments: arguments);
      return null;
    }

    if (!routes.containsKey(url)) {
      MyDialog.toast('页面不存在');
      return null;
    }

    if (url == '/') {
      navigator.popUntil(
        (route) => route.settings.name == '/',
      );
      logger.info(['tab:', arguments['route']]);

      _rootRoute = arguments['route'].toString();
      fire(RouteEvent(_rootRoute));
    } else {
      return await navigator.pushNamed(
        url,
        arguments: arguments,
      );
    }
  }

  Future<bool> clearCache() async {
    _categories = null;
    pageCaches.clear();
    _siteinfo = null;
    return await spref.clear();
  }

  startJPush() async {
    if (kIsWeb || !(Platform.isAndroid || Platform.isIOS)) {
      logger.info(r'Not android or ios, skip jpush');
      return;
    }
    if (Config.jPushAppKey.isEmpty) {
      logger.info(r'No appkey, skip jpush');
      return;
    }
    jPush = JPush();
    jPush!.addEventHandler(
      // 接收通知回调方法。
      onReceiveNotification: (Map<String, dynamic> message) async {
        logger.info("flutter onReceiveNotification: $message");
      },
      // 点击通知回调方法。
      onOpenNotification: (Map<String, dynamic> message) async {
        logger.info("flutter onOpenNotification: $message");
      },
      // 接收自定义消息回调方法。
      onReceiveMessage: (Map<String, dynamic> message) async {
        logger.info("flutter onReceiveMessage: $message");
      },
    );
    jPush!.setup(
      appKey: Config.jPushAppKey,
      channel: Config.jPushChannel,
      production: false,
      debug: true, // 设置是否打印 debug 日志
    );
    jPush!.getRegistrationID().then((rid) {
      regId = rid;
      logger.info('get registrationid: $rid');
    });
  }
}
